﻿using Mengsk.ProxyDelivery.HTTPHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Mengsk.ProxyDelivery.Service
{
    public abstract class ServiceBase<E> : IUrl where E : new()
    {
        static DateTime? dbMinTime;

        public string BaseUrl { get; set; }

        /// <summary>
        /// 使用xml方式进行远程过程调用
        /// </summary>
        /// <typeparam name="T">返回数据类型</typeparam>
        /// <param name="request">请求信息</param>
        /// <param name="url">远程url</param>
        /// <param name="headers">参数头</param>
        /// <returns></returns>
        public Result<T> DoRemoteCall<T>(Request request, string url = null, IDictionary<string, string> headers = null) where T : new()
        {
            request.AccessToken = ServiceContainer.AccessToken;
            if (string.IsNullOrWhiteSpace(request.Target))
            {
                request.Target = char.ToLower(this.GetType().Name[0]) + this.GetType().Name.Substring(1);
            }
            string xml = MSHttpRestful.DoWithRetry<string>(() => MSHttpRestful.PostStringBodyReturnString(ServiceContainer.ServerAddress + "/webservice/data/dataservice.html", request.ToXml(), Encoding.UTF8, headers));
            var ret = Result<T>.ParseXml(xml);
            if ("Success".Equals(ret.Error) == false)
            {
                throw new Exception(ret.Error);
            }
            return ret;
        }

        public Result<T> DoPost<T>(string url, IDictionary<string, string> para, IDictionary<string, string> headers = null)
        {
            para["accessToken"] = ServiceContainer.AccessToken;
            string xml = MSHttpRestful.DoWithRetry<string>(() => MSHttpRestful.PostUrlEncodeBodyReturnString(Service.ServiceContainer.ServerAddress + "/" + url, para, Encoding.UTF8, headers));
            var ret = Result<T>.ParseXml(xml);
            if ("Success".Equals(ret.Error) == false)
            {
                throw new Exception(ret.Error);
            }
            return ret;
        }

        public Result<T> DoPost<T>(string url, IDictionary<string, object> para, IList<string> fileNames, IDictionary<string, string> headers = null) where T : new()
        {
            para["accessToken"] = ServiceContainer.AccessToken;
            string xml = MSHttpRestful.DoWithRetry<string>(() => MSHttpRestful.PostMultipartFormDataBodyReturnString(Service.ServiceContainer.ServerAddress + "/" + url, para, fileNames, Encoding.UTF8, headers));
            var ret = Result<T>.ParseXml(xml);
            if ("Success".Equals(ret.Error) == false)
            {
                throw new Exception(ret.Error);
            }
            return ret;
        }

        public virtual Domain.DataCollection<E> GetAll()
        {
            Request req = new Request { Api = "getAll" };
            return this.DoRemoteCall<E>(req).ToDataCollection();
        }

        public virtual E GetById(object id)
        {
            string serviceName = char.ToLower(this.GetType().Name[0]) + this.GetType().Name.Substring(1);
            Request req = new Request { Api = "getById" };
            req.Parameters.Add(new Parameter { Name = "id", Value = id });
            Result<E> ret = this.DoRemoteCall<E>(req);
            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            return default(E);
        }


        public virtual E GetByField(string field, object value)
        {
            Request req = new Request { Api = "getByField" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            Result<E> ret = this.DoRemoteCall<E>(req);
            if (ret.Datas.Count > 0)
            {
                return ret.Datas[0];
            }
            return default(E);
        }

        public Domain.DataCollection<E> GetAllByField(string field, object value, int pageIndex, int pageSize)
        {
            Request req = new Request { Api = "getAllByField" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            req.Parameters.Add(new Parameter { Name = "pageIndex", Value = pageIndex });
            req.Parameters.Add(new Parameter { Name = "pageSize", Value = pageSize });
            Result<E> ret = this.DoRemoteCall<E>(req);
            return ret.ToDataCollection();
        }

        public Domain.DataCollection<E> GetAllByFieldLike(string field, string value, int pageIndex, int pageSize)
        {
            Request req = new Request { Api = "getAllByFieldLike" };
            req.Parameters.Add(new Parameter { Name = "field", Value = field });
            req.Parameters.Add(new Parameter { Name = "value", Value = value });
            req.Parameters.Add(new Parameter { Name = "pageIndex", Value = pageIndex });
            req.Parameters.Add(new Parameter { Name = "pageSize", Value = pageSize });
            Result<E> ret = this.DoRemoteCall<E>(req);
            return ret.ToDataCollection();
        }

        public Domain.DataCollection<E> GetPageByMap(IDictionary<string, object> param, int pageIndex, int pageSize)
        {
            Request req = new Request { Api = "getPageByMap" };
            req.Parameters.Add(new Parameter { Name = "param", Value = param });
            req.Parameters.Add(new Parameter { Name = "pageIndex", Value = pageIndex });
            req.Parameters.Add(new Parameter { Name = "pageSize", Value = pageSize });
            Result<E> ret = this.DoRemoteCall<E>(req);
            return ret.ToDataCollection();
        }

        public virtual DateTime GetDBMinTime()
        {
            if (dbMinTime != null)
            {
                return dbMinTime.Value;
            }

            Request req = new Request { Api = "getDBMinDate" };
            Result<DateTime> ret = this.DoRemoteCall<DateTime>(req);
            if (ret.Datas.Count > 0)
            {
                dbMinTime = ret.Datas[0];
                return ret.Datas[0];
            }
            throw new Exception("无法获取服务器数据库所使用的最小时间");
        }

        public virtual bool IsDBMinTime(DateTime time)
        {
            DateTime minTime = this.GetDBMinTime();
            return Math.Abs(time.Subtract(minTime).TotalDays) < 1;
        }

        public DateTime FormatToDBTime(DateTime time)
        {
            if (Math.Abs(time.Subtract(DateTime.MinValue).TotalDays) < 100)
            {
                return this.GetDBMinTime();
            }

            if (Math.Abs(time.Subtract(this.GetDBMinTime()).TotalDays) < 100)
            {
                return this.GetDBMinTime();
            }
            return time;
        }

        public string FormatTime(DateTime time)
        {
            return time.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}
